package TestPaperSystem.utils.codeClone;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import javax.annotation.Nullable;
import java.util.*;

public class CodeVerify {
    private static CodeVerify instance ;
    public static CodeVerify getInstance(){
        if(instance == null){
            instance = new CodeVerify();
        }
        return instance;
    }
    public int[] getTokenArr(String code) throws Exception{
        CompilationUnit cu = StaticJavaParser.parse(code);
        StatementVisitor visitor = new StatementVisitor();
        cu.accept(visitor, null);
        return visitor.getArrays();
    }
    public boolean getSimilarity(int[] arr_1 , int[] arr_2){
        double dotProduct = 0.0;
        double normA = 0.0;
        double normB = 0.0;
        for (int i = 0; i < arr_1.length; i++) {
            dotProduct += arr_1[i] * arr_2[i];
            normA += Math.pow(arr_1[i], 2);
            normB += Math.pow(arr_2[i], 2);
        }

        double v = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
        
        return v >= 0.95;
    }
}

class StatementVisitor extends VoidVisitorAdapter<Object>{
    //    ［标识符常量赋值数组条件表达式循环, 函数声明 , 类声明］
//          [17,   9,   17, 1,   3,   0,     4,    0,      3]
    private int[] arrays = {0,0,0,0,0,0,0,0,0};

    @Override
    public void visit(MethodDeclaration n , Object args){
        NodeList<Statement> statements = n.getBody().get().getStatements();
        Iterator<Statement> var = statements.iterator();
        while(var.hasNext()){
            Statement statement = var.next();
            this.basicStmt(statement);
        }
        super.visit(n , args);
    }

    public void basicStmt(Statement statement){

        if(statement instanceof ExpressionStmt){

            Expression expression = ((ExpressionStmt) statement).getExpression();

            if(expression instanceof VariableDeclarationExpr){

                VariableDeclarationExpr varExpression = (VariableDeclarationExpr) expression;

                @Nullable Class<?> type = expression.getClass();

                if(type != null){
                    for(VariableDeclarator variable : varExpression.getVariables()){
                        try{
                            @Nullable Expression temp = variable.getInitializer().get();
                            this.whichType(temp.getMetaModel().getTypeName());
                        }catch (Exception e){
                            System.out.println(e.getMessage());
                        }

                    }
                }
            }

            if(expression instanceof AssignExpr){

                AssignExpr assignExpr = (AssignExpr)expression;

                Expression value = assignExpr.getValue();

                this.whichType(value.getMetaModel().getTypeName());

            }
            if(expression instanceof MethodCallExpr){
                this.addArrays(new int[]{0,0,0,0,0,0,0,1,0});
            }
        }

        if(statement instanceof ForStmt){

            NodeList<Statement> body = ((BlockStmt)((ForStmt) statement).getBody()).getStatements();
            this.addArrays(new int[]{0,0,0,0,0,0,1,0,0});
            Iterator var_1 = body.iterator();
            while(var_1.hasNext()){
                Statement stmt = (Statement)var_1.next();

                this.basicStmt(stmt);

            }
        }

        if(statement instanceof  IfStmt){
            this.addArrays(new int[]{0,0,0,0,1,0,0,0,0});
            @Nullable NodeList<Statement> body = ((BlockStmt)((IfStmt) statement).getThenStmt()).getStatements();
            @Nullable NodeList<Statement> elseStmt = null;
            try {
                elseStmt = ((BlockStmt)(((IfStmt)statement).getElseStmt()).get()).getStatements();
            }catch (Exception e){
                
            }
            
            if(body != null){
                Iterator var_1 = body.iterator();
                while(var_1.hasNext()){
                    Statement stmt = (Statement)var_1.next();

                    this.basicStmt(stmt);

                }
            }
            if(elseStmt != null){
                this.addArrays(new int[]{0,0,0,0,1,0,0,0,0});
                Iterator var_1 = elseStmt.iterator();
                while(var_1.hasNext()){
                    Statement stmt = (Statement)var_1.next();

                    this.basicStmt(stmt);

                }
            }


        }

        if(statement instanceof WhileStmt){
            NodeList<Statement> body = ((BlockStmt)((WhileStmt) statement).getBody()).getStatements();
            this.addArrays(new int[]{0,0,0,0,0,0,1,0,0});
            Iterator var_1 = body.iterator();
            while(var_1.hasNext()){
                Statement stmt = (Statement)var_1.next();

                this.basicStmt(stmt);

            }

        }
    }

    public void addArrays(int[] b){
        for(int i = 0; i < this.arrays.length; i++){
            this.arrays[i] = this.arrays[i] + b[i];
        }
    }

    public void whichType(String str){
        int[] temp_arr;
        switch (str){
            case "MethodCallExpr":
                temp_arr = new int[]{1,0,1,0,0,0,0,1,0};
                break;
            case "ObjectCreationExpr":
                temp_arr = new int[]{1,0,1,0,0,0,0,0,1};
                break;
            case "ArrayInitializerExpr":
                temp_arr = new int[]{1,0,1,1,0,0,0,0,0};
                break;
            case "BinaryExpr":
                temp_arr = new int[]{1,0,1,0,0,1,0,0,0};
                break;
            default:
                temp_arr = new int[]{1,1,1,0,0,0,0,0,0};
                break;
        }
        this.addArrays(temp_arr);
    }

    public int[] getArrays() {
        return arrays;
    }


}
